/* @flow */

import { Dep } from './dep'
import { VNode } from '../vdom/vnode'

import { isObject, isPlainObject, noop, isUndef, isDef } from '../../util/index'

export let shouldObserve = true

//打开和关闭observe的开关
//instance/lifecycle.js/updateChildComponent方法用到
export function toggleObserving(value) {
    shouldObserve = value
}

export function observe(value) {
    if (!isObject(value) || value instanceof VNode) {
        return
    }
    let ob
    if (
        (Array.isArray(value) || isPlainObject(value)) &&
        !value._isVue //不是Vue构造器
    ) {
        ob = walk(value)
    }
    return ob
}


export function walk(val) {
    if (shouldObserve) {
        if (Array.isArray(val)) {
            let arr = []
            for (const item of val) {
                arr.push(walk(item))
            }
            return defineReactive(arr)
        } else if (typeof val === 'object') {
            for (const key in val) {
                val[key] = walk(val[key])
            }
            return defineReactive(val)
        } else {
            return val
        }
    }else{
        return val
    }
}


export function defineReactive(obj) {
    if (isUndef(obj) || isDef(obj.__ob__)) { //example props data "message: { type: null } " will be go to error
        return obj
    }
    const deps = {}
    Object.defineProperty(obj, '__ob__', {
        enumerable: false,
        value: {
            value: obj,
            deps: deps,
        },
        setter: noop,
        getter: function () {
            return obj
        }
    })

    return new Proxy(obj, {
        get: function reactiveGetter(_, propKey) {
            const value = obj[propKey]
            if (Dep.target) {
                let dep
                if (!deps[propKey]) {
                    dep = new Dep()
                    deps[propKey] = dep
                } else {
                    dep = deps[propKey]
                }
                dep.exp = propKey
                dep.depend()
            }
            return value
        },
        set: function reactiveSetter(_, propKey, newVal) {
            let value = obj[propKey]
            if (newVal === value || (newVal !== newVal && value !== value)) {
                return true
            }
            value = walk(newVal)
            obj[propKey] = value
            deps[propKey] && deps[propKey].notify()
            return true
        }
    })

}
